flbuff = LibStub("AceAddon-3.0"):NewAddon("flaming buffs", "AceEvent-3.0", "AceConsole-3.0")

--[[fix
check warr for blood pact?
distinguish 6% and 10% sp?
--]]

--[[settings
include dead people in counts? out of range?
announce food/flasks
snark?
--]]

--[[todo
--remind all button
--snarky buff messages
--]]

--[[maybe
--expiration warner?
--weap enchants - could record last scanned?
--would be nice to see which players are missing wat, but how?! extra tabling or iterate
--]]

--VARIABLES
local roster = {}
local pets = {}
local size = 0
local rosterchanged = true

local buffbuttons = {}
local extrabuttons = {}

local col= 0
local row = 0

local inspectqueue = {}
local scanned = 0
local interval = 5

local debugt = 4 --all! 1=error, 2=basic, 3=detailed

--LISTS
local RAID_BUFFS = {
	--Raid-wide buffs
	["Power Word: Fortitude"] = {
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\Spell_Holy_WordFortitude",
	},
	["Commanding Shout"] = { --backup
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\ability_warrior_rallyingcry",
	},
	["Fortitude"] = { --backup x2
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\Spell_Holy_WordFortitude",
	},
	["Shadow Protection"] = {
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\spell_holy_prayerofshadowprotection",
	},
	["MotW/Kings"] = {
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\Spell_Magic_GreaterBlessingofKings",
	},
	["Blessing of Forgotten Kings"] = { --backup
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\spell_magic_magearmor",
	},
	["Blessing of Might"] = {
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\Spell_Holy_GreaterBlessingofKings",
	},
	["Brilliance"] = { --Arcane Brilliance, Dalaran Brilliance
		buffer = nil,
		buffed = {},
		texture = "Interface\\Icons\\Spell_Holy_ArcaneIntellect",
	},		
}

local SELF_BUFFS = {
	--Self/single player buffs
	["Focus Magic"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_arcane_studentofmagic",
		notself = true,
	},
	["Dark Intent"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_warlock_focusshadow",
		notself = true,
	},
	["Aspect of the Hawk"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_nature_ravenform",
		perm = true,
	},
	["Vigilance"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Ability_Warrior_Vigilance",
		notself = true,
	},
	["Righteous Fury"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_holy_sealoffury",
		perm = true,
	},
	["Shadowform"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Spell_Shadow_Shadowform",
		perm = true,
	},
	["Vampiric Embrace"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_shadow_unsummonbuilding",
		perm = true,
	},
	["Soul Link"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_shadow_gathershadows",
		perm = true,
	},
	["Earth Shield"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_nature_skinofearth",
		notself = true,
	},
	["Inner Fire/Will"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Spell_Holy_InnerFire",
		perm = true,
	},
	["Pet"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\ability_hunter_beastcall",
		perm = true,
	},
	["Well Fed"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\spell_misc_food",
	},
	["Flask"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\inv_potion_112",
	},
	["Seal"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Spell_Holy_SealOfVengeance",
	},
	["Aura"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\SPELL_HOLY_DEVOTIONAURA",
		perm = true,
	},
	["Warlock armor"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Spell_Fire_FelFireward",
	},
	["Mage armor"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\ABILITY_MAGE_MOLTENARMOR",
	},
	["Weapon enchant"] = {
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Spell_Fire_FlameTounge",
	},
	["Water/Lightning Shield"] = { --***
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Ability_Shaman_WaterShield",
	},
	["Presence"] = { --***
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\Spell_Deathknight_UnholyPresence",
		perm = true,
	},
	["Healthstone"] = { --***
		buffers = {},
		buffed = {},
		missing = {},
		texture = "Interface\\Icons\\inv_stone_04",
		perm = true,
	},
}

local GENERIC = {
	["Blessing of Kings"] = "MotW/Kings",
	["Mark of the Wild"] = "MotW/Kings",
	["Blood Pact"] = "Power Word: Fortitude",
	["Lightning Shield"] = "Water/Lightning Shield",
	["Water Shield"] = "Water/Lightning Shield",
	["Seal of Righteousness"] = "Seal",
	["Seal of Insight"] = "Seal",
	["Seal of Truth"] = "Seal",
	["Arcane Brilliance"] = "Brilliance",
	["Dalaran Brilliance"] = "Brilliance",
	["Fel Armor"] = "Warlock armor",
	["Demon Armor"] = "Warlock armor",
	["Mage Armor"] = "Mage armor",
	["Molten Armor"] = "Mage armor",
	["Frost Armor"] = "Mage armor",
	
	["Inner Fire"] = "Inner Fire/Will",
	["Inner Will"] = "Inner Fire/Will",
	["Devotion Aura"] = "Aura",
	["Concentration Aura"] = "Aura",
	["Resistance Aura"] = "Aura",
	["Retribution Aura"] = "Aura",	
	["Blood Presence"] = "Presence",	
	["Frost Presence"] = "Presence",	
	["Unholy Presence"] = "Presence",
	["Flask of the Winds"] = "Flask",
	["Flask of the Draconic Mind"] = "Flask",
	["Flask of Titanic Strength"] = "Flask",
	["Flask of Steelskin"] = "Flask",
	["Flask of Flowing Water"] = "Flask",
}

local BUFF_LIST = {
	["3% damage"] = false,
	["Str/Agi"] = false,
	["10% AP"] = false,
	Armor = false,
	["5% crit"] = false,
	["MP5"] = false,
	["Physical haste"] = false,
	["Pushback protection"] = false,
	Replenishment = false,
	["Spell haste"] = false,
	["6% spell power"] = false,
	["10% spell power"] = false,
	Resistance = false,
	Stamina = false,
	["Stat multiplier"] = false,
}

local DEBUFF_LIST = {
	["Armor reduc"] = false,
	["8% spell dmg"] = false,
	["4% phys dmg"] = false,
	["Bleed damage"] = false,
	["5% spell crit"] = false,
}

--OPTS
local options = {
	name = "flaming buffs",
	type = "group",
	childGroups="tab",
	args = {
		halp = {
			type="description",
			width="full",
			name = "\nAvailable buffs are automatically determined based on the classes/specs in your raid.\n\nMain window (accessed by /buffs)\n- The top section shows raid-wide buffs; the second shows buffs placed on a single player.\n- Icons will be faded out if the buff isn't missing for anyone.  Otherwise, they display the number of people missing.\n- Hovering over an icon for a buff that needs buffing will show you the person/people responsible.\n- Right clicking an icon will send a whisper reminder to the responsible person/people.\n- The buff list window shows you which major raid buffs you have & don't have.\n",
			order=2,
		},
		announce = {
				name = "Announce feasts, cauldrons and soulwells",
				type = "toggle",
				width="full",
				set = function(info,val) flbuff.db.global.announce = val end,
				get = function(info) return flbuff.db.global.announce end,
		},
		deaths = {
				name = "Announce deaths (to you) and battle rezzes (to raid)",
				type = "toggle",
				width="full",
				set = function(info,val) flbuff.db.global.deaths = val end,
				get = function(info) return flbuff.db.global.deaths end,
		},
		includeall = {
				name = "Include well fed and flask buffs",
				type = "toggle",
				width="full",
				set = function(info,val) flbuff.db.global.includeall = val end,
				get = function(info) return flbuff.db.global.includeall end,
		},
		auto = {
				name = "Show window when combat ends",
				type = "toggle",width="full",
				set = function(info,val) flbuff.db.global.auto = val end,
				get = function(info) return flbuff.db.global.auto end,
		},
	},
}

local defaults = {
	global = {
		announce = true,
		deaths = true,
		rude = true,
		includeall = true,
		auto = true,
	}
}

--INIT/ENABLE
function flbuff:OnInitialize()
	FLBuffFrame:SetScale(0.7)
	--FLBuffFrameBuffs:SetWidth(60)
	self.db = LibStub("AceDB-3.0"):New("fbuffs", defaults)
	self.log = LibStub("AceDB-3.0"):New("flbufflogdb")

end

function flbuff:OnEnable()
	LibStub("AceConfig-3.0"):RegisterOptionsTable("flbuff", options)
	LibStub("AceConfigDialog-3.0"):AddToBlizOptions("flbuff", "flaming buffs")
	self:RegisterChatCommand("buffs", "ShowFrame")
	self:RegisterChatCommand("fbuffs", function() InterfaceOptionsFrame_OpenToCategory("flaming buffs") end)
end

--HOW TO
function flbuff:ShowHelp()
	self:Print("Help:\n- |cffffff78/buffs|r to view main frame\n- Buffs are scanned & refreshed every 5 sec or can be manually refreshed\n- Hiding the frame will disable buff scanning.  This is done automatically whenever you enter combat.")
end

--SCANNER
local scanner = CreateFrame("Frame", "FLScanner")
scanner:SetScript("OnUpdate", function(self, elapsed) flbuff:Scanning(self, elapsed) end)
scanner:Hide()
function flbuff:Scanning(self, elapsed)
	interval = interval+elapsed
	if interval>4 then
		flbuff:debug(2, "SCANNER", "calling refresh")
		interval = 0
		flbuff:Refresh()
	end
end

local tinterval = 0
local tscanner = CreateFrame("Frame", "FLtcanner")
tscanner:SetScript("OnUpdate", function(self, elapsed) flbuff:Tcanning(self, elapsed) end)
tscanner:Hide()
function flbuff:Tcanning(self, elapsed)
	tinterval = tinterval+elapsed
	if #inspectqueue>0 and tinterval>3 then
		flbuff:debug(3, "INSPECT", "queue length "..#inspectqueue)
		tinterval = 0
		flbuff:ScanNextPlayer()
	end
end

--ROSTER
function flbuff:CreateRoster()
	size = GetNumRaidMembers()
	flbuff:debug(3, "ROSTER", "Creating roster, size:", size)
	for i=1, size do
		local name, _, _, _, class = GetRaidRosterInfo(i)
		roster[name] = roster[name] or {["class"] = class, ["spec"] = 0}
		roster[name].index = i
		roster[name].unitid = "raid"..i
		local petname = roster[name].unitid.."pet"
		if UnitExists(roster[name].unitid.."pet") then 
			if UnitPlayerOrPetInRaid(roster[name].unitid.."pet") then
				petname = UnitName(petname)
				pets[petname] = name
			end
		end
		--flbuff:debug(3, "roster", name, "raid"..i)
		table.insert(inspectqueue, name)
	end
	rosterchanged = false
	scanned = 0
	self:RegisterEvent("INSPECT_READY")
	flbuff:CompileRaidBuffs()
	tscanner:Show()
end

function flbuff:RAID_ROSTER_UPDATE()
	flbuff:debug(3,"RAID_ROSTER_UPDATE")
	if UnitInRaid("player") then
		if FLBuffFrame:IsShown() then
			if (GetNumRaidMembers() ~= size) or rosterchanged then --current or recorded roster change
				--flbuff:debug(3,"raid_roster_update", "Refreshing roster & buffs")
				for a, b in pairs(BUFF_LIST) do
					BUFF_LIST[a] = false
				end
				for c, d in pairs(RAID_BUFFS) do
					wipe(d.buffed)
					d.buffer=nil
					d.missing=0
				end
				for e, f in pairs(SELF_BUFFS) do
					wipe(f.buffed)
					wipe(f.buffers)
					wipe(f.missing)
				end
				for name, bl in pairs(roster) do
					if not UnitInRaid(name) then
						roster[name] = nil
					end
				end
				wipe(inspectqueue)
				flbuff:CreateRoster()
			end
		else --if frame is hidden, just note and stop tracking
			flbuff:HideFrame()
			rosterchanged = true
			self:UnregisterEvent("RAID_ROSTER_UPDATE")
			flbuff:debug(3, "raid_roster_update", "Roster changed while frame hidden - recorded; unregistering")
		end
	else
		flbuff:HideFrame()
		self:UnregisterAllEvents()
	end
end

--TALENT SCANNING
function flbuff:ScanNextPlayer()
	local unitid = roster[inspectqueue[1]].unitid
	if CanInspect(unitid, false) then
		flbuff:debug(3, "INSPECT", "notifying inspect for "..inspectqueue[1])
		NotifyInspect(unitid)
	elseif #inspectqueue>1 then
		flbuff:debug(3, inspectqueue[1], "inspectqueue", "not inspectable; moving to back of queue")
		local name = table.remove(inspectqueue, 1)
		table.insert(inspectqueue, #inspectqueue+1, name)
	end
	scanned = scanned+1
end

function flbuff:INSPECT_READY(event, GUID)
	local name = select(6, GetPlayerInfoByGUID(GUID))
	flbuff:debug(3, "INSPECT", "Inspect ready for "..name.." first is "..(inspectqueue[1] or "queue empty").." aka "..(string.match(inspectqueue[1], "^[^%-]+") or "none"))
	if inspectqueue[1] and name==string.match(inspectqueue[1], "^[^%-]+") then
		local spec = GetPrimaryTalentTree(true)
		flbuff:debug(3, "INSPECT", inspectqueue[1].." spec is "..spec..", on file: "..roster[inspectqueue[1]].spec)
		if spec ~= roster[inspectqueue[1]].spec then
			roster[inspectqueue[1]].spec = spec	
			local TALENT_AUTOBUFFS = {
				Hunter = {15, 11, 17}, --3% dmg, 10%AP, phys haste
				["Death Knight"] = {11, 13, 1}, --10% AP, phys haste
			}
			if TALENT_AUTOBUFFS[roster[inspectqueue[1]].class] then
				_, _, _, _, rank, maxrank = GetTalentInfo(spec, TALENT_AUTOBUFFS[roster[inspectqueue[1]].class][spec], true)
				if rank==maxrank then
					roster[inspectqueue[1]].tbuff = true 
				end	
			end
		end
		flbuff:AddTalentBuffs(inspectqueue[1])
		table.remove(inspectqueue, 1)
		if #inspectqueue==0 then
			self:Print("finally done inspecting!")
			tscanner:Hide()
			self:UnregisterEvent("INSPECT_READY")
		end		
	end
end

function flbuff:AddTalentBuffs(name)
	flbuff:debug(3, "INSPECT", "Adding talent buffs for "..name)
	if roster[name].class=="Death Knight" then
		if roster[name].spec==1 and roster[name].tbuff then
			BUFF_LIST["10% AP"] = true
		elseif roster[name].spec==2 then
			DEBUFF_LIST["Physical haste"] = true
			if roster[name].tbuff then
				BUFF_LIST["Physical haste"] = true
			end
		end
	elseif roster[name].class=="Druid" then
		if roster[name].spec==1 then
			DEBUFF_LIST["8% spell dmg"] = true
		elseif roster[name].spec==2 then
			BUFF_LIST["5% crit"] = true
			DEBUFF_LIST["Bleed damage"] = true
			DEBUFF_LIST["Armor reduc"] = true
		elseif roster[name].spec==3 then
			BUFF_LIST.Replenishment = true
		end
	elseif roster[name].class=="Paladin" then
		if roster[name].spec ==3 then
			BUFF_LIST["3% damage"] = true
			BUFF_LIST.Replenishment = true
		elseif roster[name].spec==2 then
			table.insert(SELF_BUFFS["Righteous Fury"].buffers, name)
		end
	elseif roster[name].class=="Mage" then
		if roster[name].spec==1 then
			BUFF_LIST["3% damage"] = true
			table.insert(SELF_BUFFS["Focus Magic"].buffers, name)
		elseif roster[name].spec==2 then
			DEBUFF_LIST["5% spell crit"] = true
		end
	elseif roster[name].class=="Warlock" and roster[name].spec==3 then
		BUFF_LIST["10% spell power"] = true
		DEBUFF_LIST["8% spell dmg"] = true
	elseif roster[name].class=="Hunter" and roster[name].tbuff then
		if roster[name].spec==1 then
			BUFF_LIST["3% damage"] = true
		elseif roster[name].spec==2 then
			BUFF_LIST["10% AP"] = true
		elseif roster[name].spec==3 then
			BUFF_LIST["Physical haste"] = true
		end
	elseif roster[name].class=="Shaman" then
		if roster[name].spec==3 then
			table.insert(SELF_BUFFS["Earth Shield"].buffers, name)
		elseif roster[name].spec==1 then
			BUFF_LIST["5% crit"] = true
			BUFF_LIST["10% spell power"] = true
		elseif roster[name].spec==2 then
			BUFF_LIST["10% AP"] = true
		end
	elseif roster[name].class=="Warrior" then
		DEBUFF_LIST["Armor reduc"] = true
		if roster[name].spec==1 then
			DEBUFF_LIST["4% phys dmg"] = true
			DEBUFF_LIST["Bleed damage"] = true
		elseif roster[name].spec==2 then
			BUFF_LIST["5% crit"] = true
		elseif roster[name].spec==3 then
			table.insert(SELF_BUFFS["Vigilance"].buffers, name)
		end	
	elseif roster[name].class=="Priest" and roster[name].spec==3 then
		table.insert(SELF_BUFFS["Shadowform"].buffers, name)
		table.insert(SELF_BUFFS["Vampiric Embrace"].buffers, name)
		BUFF_LIST["Spell haste"] = true
		BUFF_LIST.Replenishment = true
	elseif roster[name].class == "Rogue" and roster[name].spec==3 then
		DEBUFF_LIST["Bleed damage"] = true
	end
	flbuff:CreateButtons()
end	

--AVAILABLE BUFFS
function flbuff:CompileRaidBuffs()
	flbuff:debug(3, "COMPILING")
	local warrs = {}
	for name, nta in pairs(roster) do
		if self.db.global.includeall then
			table.insert(SELF_BUFFS["Flask"].buffers, name)
			table.insert(SELF_BUFFS["Well Fed"].buffers, name)
		end
		flbuff:debug(3, "COMPILING", name)
		flbuff:debug(3, "COMPILING", "stat mult", tostring(BUFF_LIST["Stat multiplier"]))
		if nta.class=="Death Knight" then
			BUFF_LIST["Str/Agi"] = true
			table.insert(SELF_BUFFS["Presence"].buffers, name)
		elseif nta.class=="Druid" then
			RAID_BUFFS["MotW/Kings"].buffer = name
			flbuff:debug(3, "RAID_BUFFS", "Mark of the Wild", name)
			BUFF_LIST["Stat multiplier"] = true
		elseif nta.class == "Paladin" then
			if not RAID_BUFFS["Blessing of Might"].buffer then
				RAID_BUFFS["Blessing of Might"].buffer = name
				flbuff:debug(3, "RAID_BUFFS", "Blessing of Might", name)
				BUFF_LIST["10% AP"] = true
				BUFF_LIST["MP5"] = true
			elseif not BUFF_LIST["Stat multiplier"] then
				RAID_BUFFS["MotW/Kings"].buffer = name
				flbuff:debug(3, "RAID_BUFFS", "Blessing of Kings", name)
				BUFF_LIST["Stat multiplier"] = true
			end
			table.insert(SELF_BUFFS["Seal"].buffers, name) --***
		elseif nta.class == "Mage" then
			RAID_BUFFS["Brilliance"].buffer = name --***
			table.insert(SELF_BUFFS["Mage armor"].buffers, name) --***
			--flbuff:debug(3, "SELF_BUFFS", "mage armor", name)
			BUFF_LIST["6% spell power"] = true
			BUFF_LIST["MP5"] = true
		elseif nta.class == "Warlock" then
			table.insert(SELF_BUFFS["Soul Link"].buffers, name)
			table.insert(SELF_BUFFS["Dark Intent"].buffers, name)
			table.insert(SELF_BUFFS["Warlock armor"].buffers, name) --***
			table.insert(SELF_BUFFS["Pet"].buffers, name) --***
			if not SELF_BUFFS["Healthstone"].buffers[1] then
				table.insert(SELF_BUFFS["Healthstone"].buffers, name)
			end
			--flbuff:debug(3, "SELF_BUFFS", "warlock buffs", name)
		elseif nta.class == "Hunter" then
			table.insert(SELF_BUFFS["Aspect of the Hawk"].buffers, name)
			table.insert(SELF_BUFFS["Pet"].buffers, name) --***
		elseif nta.class == "Shaman" then
			--table.insert(RAID_BUFFS["Weapon enchant"].buffers, name) --***
			table.insert(SELF_BUFFS["Water/Lightning Shield"].buffers, name) --***
		elseif nta.class == "Warrior" then
			table.insert(warrs, name)
			DEBUFF_LIST["Armor reduc"] = 2
		--elseif nta.class == "Rogue" then
			--table.insert(RAID_BUFFS["Weapon enchant"].buffers, name) --***
		elseif nta.class == "Priest" then
			table.insert(SELF_BUFFS["Inner Fire/Will"].buffers, name) --***
			RAID_BUFFS["Power Word: Fortitude"].buffer = name
			RAID_BUFFS["Shadow Protection"].buffer = name
			BUFF_LIST["Stamina"] = true
		end
	end
	flbuff:SetFlexibleBuffs(warrs)
end

function flbuff:SetFlexibleBuffs(warrs)
	--flbuff:debug(3, "RAID_BUFFS", "setting flexible buffs")
	if not BUFF_LIST.Stamina then
		if #warrs>0 then
			RAID_BUFFS["Commanding Shout"].buffer = warrs[1]
			BUFF_LIST.Stamina = true
			table.remove(warrs, 1)
		else --add a check
			RAID_BUFFS["Fortitude"].buffer = GetUnitName("player")
			BUFF_LIST.Stamina = true
		end
	end
	if (not BUFF_LIST["Str/Agi"] and #warrs>0) then
		BUFF_LIST["Str/Agi"] = true
	end
	if not BUFF_LIST["Stat multiplier"] then
		RAID_BUFFS["Blessing of Forgotten Kings"].buffer = GetUnitName("player")
		self:Print("forgotten kings "..RAID_BUFFS["Blessing of Forgotten Kings"].buffer)
		BUFF_LIST["Stat multiplier"] = true
	end
	--Maybe make a popup to assign totems, auras, & pets?
	flbuff:CreateButtons()
end


function flbuff:CreateButtons()
	--flbuff:debug(3, "UI", "creating buttons")
	for k, v in pairs(RAID_BUFFS) do
		if v.buffer and not buffbuttons[k] then
			flbuff:CreateButton(k)
		end
	end
	for k, v in pairs(SELF_BUFFS) do
		if v.buffers[1] and not buffbuttons[k] then
			flbuff:CreateButton(k)
		end
	end
	flbuff:UpdateBuffButtons()

end

function flbuff:CreateButton(k)
	--flbuff:debug(3, "UI", "creating button for", k)
	local	b = CreateFrame("Button", k, FLBuffFrame)
	buffbuttons[k] = b
	b:SetSize(64, 64)
	b:RegisterForClicks("AnyUp", "AnyDown")
	local texture
	if RAID_BUFFS[k] then
		texture = RAID_BUFFS[k].texture
	else
		texture = SELF_BUFFS[k].texture
	end
	b:SetNormalTexture(texture)
	b:SetScript("OnMouseUp", function(self, button) 
					--if button=="LeftButton" then
						--flsys:ShowSysAward(name) 
					if button=="RightButton" then
						flbuff:Remind(k)
					end
				end)
	b:SetScript("OnEnter", function(self, motion) flbuff:ShowTooltip(k) end)
	b:SetScript("OnLeave", function(self, motion) GameTooltip:Hide() end)
	b:SetScale(0.35)
	b:SetText(" ")
	b:GetFontString():SetFont("Fonts\\ARIALN.TTF", 40, "THICKOUTLINE")
end

function flbuff:UpdateBuffButtons()
	--reposition buttons, deleting any that are no longer relevant, set frame size ----DOESN"T ACTUALLY DELETE> and need to hide!
	col= 0
	row = 0
	for buff, etc in pairs(RAID_BUFFS) do --double iterate so raid buffs & self buffs are in sep groups
		if buffbuttons[buff] then
			if RAID_BUFFS[buff].buffer then
				col = col+1
				buffbuttons[buff]:SetPoint("TOPLEFT", 25+(col-1)*72, -90-row*72)
				buffbuttons[buff]:Show()
			else
				buffbuttons[buff]:Hide()
			end
		end
		if col==5 then
			col = 0
			row = row+1
		end
	end
	for buff, etc in pairs(SELF_BUFFS) do
		if buffbuttons[buff] then
			if #SELF_BUFFS[buff].buffers>0 then
				col = col+1
				buffbuttons[buff]:SetPoint("TOPLEFT", 25+(col-1)*72, -90-row*72)
				buffbuttons[buff]:Show()
			else
				buffbuttons[buff]:Hide()
			end
		end
		if col==5 then
			col = 0
			row = row+1
		end
	end
 --[[for buff, b in pairs(buffbuttons) do
		if RAID_BUFFS[buff] then
			if RAID_BUFFS[buff].buffer then
				col = col+1
				b:SetPoint("TOPLEFT", 30+col*72, -60-row*72)
			else
				b = nil
			end
		elseif SELF_BUFFS[buff] then
			if #SELF_BUFFS[buff].buffers>0 then
				col = col+1
				b:SetPoint("TOPLEFT", 30+col*72, -60-row*72)
			else
				b = nil
			end
		end
		if col==5 then
			col = 0
			row = row+1
		end
	end--]]
	FLBuffFrame:SetHeight(80+row*40)
end


--PRESENT BUFFS
function flbuff:Refresh() --clear all buffed, scan players & record buffs
	--flbuff:debug(3, "REFRESH BUFFS", "wipe(t.buffed) t.missing =0, wipe(t.buffed) wipe(t.missing)")
	for n, t in pairs(RAID_BUFFS) do
		wipe(t.buffed)
		t.missing = 0
	end
	for n, t in pairs(SELF_BUFFS) do
		wipe(t.buffed)
		wipe(t.missing)
	end
	flbuff:GetPlayerBuffs()
end

function flbuff:GetPlayerBuffs()
	--flbuff:debug(3, "GET PLAYER BUFFS", "Getting current player buffs")
	for name, nta in pairs(roster) do
		for i=1, 32 do
			local buffname, _, _, _, _, duration, expiration, caster, _, _, spellID = UnitBuff(nta.unitid, i)
			if buffname then
				caster = caster or "none"
				local expiring = false
				if expiration then
					if duration>600 and (expiration-GetTime())<300 then
						expiring = true
					end
				end
				--self:Print(buffname..caster)
				--flbuff:debug(1, name, nta.unitid, buffname, caster)
				if GENERIC[buffname] then
					buffname = GENERIC[buffname]
				end
				if not expiring then
					if RAID_BUFFS[buffname] and RAID_BUFFS[buffname].buffer then
							table.insert(RAID_BUFFS[buffname].buffed, name)
							--flbuff:debug(3, "RAID_BUFFS", buffname, "buffed count ", #RAID_BUFFS[buffname].buffed)
					elseif SELF_BUFFS[buffname] then
						if SELF_BUFFS[buffname].notself then
							local castername, realm = UnitName(caster)
							if realm then
								castername = castername.."-"..realm
							end
							SELF_BUFFS[buffname].buffed[castername] = true
						else
							SELF_BUFFS[buffname].buffed[name] = true
						end
					end
				end
			else
				--self:debug(3, "PLAYER BUFFS", name, i, "no buffname")
				break
			end
		end
		if nta.class=="Hunter" or nta.class=="Warlock" then
			if UnitExists(nta.unitid.."pet") then
				SELF_BUFFS["Pet"].buffed[name] = true
			end
		end
	end
	local hs = GetItemCount("Healthstone", false, false)
	flbuff:debug(hs.." healthstones found in inv")
	if hs>0 and SELF_BUFFS["Healthstone"].buffers[1] then
		flbuff:debug("Inserting "..SELF_BUFFS["Healthstone"].buffers[1].." into buffed table. ")
		table.insert(SELF_BUFFS["Healthstone"].buffed, SELF_BUFFS["Healthstone"].buffers[1])
	end
	flbuff:RefreshButtons()
end

function flbuff:RefreshButtons()
	flbuff:debug(3, "UPDATE BUFF BUTTONS")
	for buffname, v in pairs(buffbuttons) do
		--flbuff:debug(3, "RAID_BUFFS SELF_BUFFS", "updating button for "..buffname)
		if RAID_BUFFS[buffname] and RAID_BUFFS[buffname].buffer then
			RAID_BUFFS[buffname].missing = size-#RAID_BUFFS[buffname].buffed
			--flbuff:debug(3, buffname, "size", size, "buffed", #RAID_BUFFS[buffname].buffed)
			if RAID_BUFFS[buffname].missing>0 then
				buffbuttons[buffname]:SetText(RAID_BUFFS[buffname].missing)
			else
				buffbuttons[buffname]:SetText(" ")
			end
		elseif SELF_BUFFS[buffname] and SELF_BUFFS[buffname].buffers[1] then
			for i, n in ipairs(SELF_BUFFS[buffname].buffers) do
				--flbuff:debug(3, "SELF_BUFFS" ,"checking self buffer #"..i..n)
				if not SELF_BUFFS[buffname].buffed[n] then
					--flbuff:debug(3,"SELF_BUFFS", n,"not buffed, adding to missing table")
					table.insert(SELF_BUFFS[buffname].missing, n)
				end
			end
			if #SELF_BUFFS[buffname].missing>0 then
				buffbuttons[buffname]:SetText(#SELF_BUFFS[buffname].missing)
			else
				buffbuttons[buffname]:SetText(" ")
			end
		end
		if buffbuttons[buffname]:GetText()==" " then
			buffbuttons[buffname]:SetAlpha(.5)
		else
			buffbuttons[buffname]:SetAlpha(1)
		end
	end
	if not FLBuffFrame:IsShown() then
		flbuff:ShowFrame()
	end
end

function flbuff:ShowTooltip(buffname)
	flbuff:debug(3, "TOOLTIP", "hovering over "..buffname)
	local ttext = buffname
	if RAID_BUFFS[buffname] and RAID_BUFFS[buffname].buffer then
		ttext = ttext.."\n\nBuffer: |cffffffff"..RAID_BUFFS[buffname].buffer
	elseif SELF_BUFFS[buffname].missing and SELF_BUFFS[buffname].missing[1] then
		ttext = ttext.."\n\n|cffffffff"..table.concat(SELF_BUFFS[buffname].missing, "\n")
	end
	GameTooltip:SetOwner(buffbuttons[buffname], "ANCHOR_RIGHT")
	GameTooltip:SetText(ttext)
	GameTooltip:Show()
end

--COMBAT LOG
function flbuff:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, subevent, hideCaster, srcGUID, srcname, srcflags, srcRaidFlags, dstGUID, dstname, dstflags, dstRaidFlags, spellID, spellname)
	if not subevent then
		return
	end
	if not UnitAffectingCombat("player") and self.db.global.announce then
		local objname, expense
		if subevent == "SPELL_CREATE" then -- Seafood Magnifique Feast 3m
			if spellID == 87644 then
			objname = "Seafood Magnifique Feast"
			expense="feast"
			elseif spellID ==29893 then
				objname = "Soulwell"
			end
		elseif subevent=="SPELL_CAST_START" then
			if spellID == 92649 then -- Cauldron of Battle 10m
				objname = "Cauldron of Battle"
				expense="cauldron"
			elseif spellID == 92712 then -- Big Cauldron of Battle 10m
				objname = "Big Cauldron of Battle"
				expense="bigcauldron"
			end
		elseif spellID == 29893 and dstname=="Soul Portal" then -- Ritual of Souls 3m
			flbuff:debug(3, "Ritual of Souls")
			objname = "Soulwell"
		end
		if objname and srcname then
			flbuff:Announce(srcname.." has prepared a "..objname)
		end
		--if expense and flsys then
		--	flsys:Expense(expense)
	--	end
	elseif self.db.global.deaths then--in combat
		if subevent == "UNIT_DIED" and dstname and roster[dstname] then
			flbuff:debug(3, "DEATH OMG", dstname)
			DEFAULT_CHAT_FRAME:AddMessage(dstname.." died!")
			PlaySoundFile("Sound\\Event Sounds\\Wisp\\WispPissed1.wav")
		elseif subevent=="SPELL_CAST_SUCCESS" and (spellID==20484 or spellID==20707 or spellID==61999) then --rebirth, soulstone, raise ally
			flbuff:Announce(srcname.." battle rezzed "..dstname)
		end
	end
end

--WHISPER/ANNOUNCE
function flbuff:Remind(buffname)
	flbuff:debug(3, "REMIND", buffname)
	if RAID_BUFFS[buffname] and RAID_BUFFS[buffname].missing>0 then
		SendChatMessage("Missing "..buffname..": "..RAID_BUFFS[buffname].missing, "WHISPER", nil, RAID_BUFFS[buffname].buffer)
	elseif SELF_BUFFS[buffname] and #SELF_BUFFS[buffname].missing>0 then
		for i, n in ipairs(SELF_BUFFS[buffname].missing) do
			local bmsg = buffname.." is missing or about to expire."
			if buffname.suffix then
				bmsg=bmsg..buffname.suffix
			end
			SendChatMessage(bmsg, "WHISPER", nil, n)--n)
		end
	end
end

function flbuff:RemindAll()
	for k, b in pairs(buffbuttons) do
		flbuff:Remind(k)
	end	
end

function flbuff:Announce(wut)
	flbuff:debug(3, "Announce", wut)
	if IsRaidLeader() or IsRaidOfficer() then
		SendChatMessage(wut, "RAID_WARNING")
	end
end

function flbuff:MildAnnounce(wat)
	SendChatMessage(wat, "RAID", nil)
end

--SHOW/HIDE
function flbuff:HideFrame()
	flbuff:debug(3, "END", "---HIDING FRAME---")
	FLBuffFrame:Hide()
	scanner:Hide()
	self:RegisterEvent("PLAYER_REGEN_ENABLED", "TimerShow")
end

function flbuff:TimerShow() --show the frame after a 5s delay for auto-ss
	scanner:Show()
end

function flbuff:ShowFrame()
	if (not UnitInRaid("player")) then
		self:Print("Buff tracker cannot be shown while not in a raid.")
	elseif not FLBuffFrame:IsShown() then
		flbuff:debug(3, "START", "---SHOWING FRAME---")
		self:RegisterEvent("PLAYER_REGEN_DISABLED", "HideFrame")
		self:RegisterEvent("RAID_ROSTER_UPDATE")
		if self.db.global.deaths or self.db.global.announce then
			self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
		end
		FLBuffFrame:Show()
		scanner:Show()
		flbuff:RAID_ROSTER_UPDATE()
	end
end

function flbuff:debug(logtype, ...)
	table.insert(flbuff.log.profile, date("%m/%d %H:%M:%S").."---"..logtype.."---"..(table.concat({...}, " ")))
end

--TESTING
function flbuff:TestSelf()
	for i=1, 32 do
		local buffname, _, _, _, _, duration, expiration, caster, _, _, spellID = UnitBuff("player", i)
		if buffname then
			caster = caster or "none"
			self:Print(buffname..caster)
			local expiring = false
			if expiration then
				if duration>500 and (expiration-GetTime()<300) then
					expiring = true
				end
			end
			self:Print(expiring)
		end
	end
end
			
--[[
Bloodlust / Heroism
Physical Damage Incoming

Armor
Bleed Damage
Healing
Physical Vulnerability
Spell Critical Strike Chance
Spell Damage Taken
--]]
--[[
local roguewepbuffs = {
	L["( Poison ?[IVX]*)"], -- Anesthetic Poison, Deadly Poison [IVX]*, Crippling Poison [IVX]*, Wound Poison [IVX]*, Instant Poison [IVX]*, Mind-numbing Poison [IVX]*
}

local shamanwepbuffs = {
	L["(Flametongue)"], -- Shaman self buff
	L["(Earthliving)"], -- Resto Shaman self buff
	L["(Frostbrand)"], -- Shaman self buff
	L["(Rockbiter)"], -- Shaman self buff
	L["(Windfury)"], -- Shaman self buff
}
--]]